Import mojo
Import agfx

class TVirtualStick
private
	' the coordinates and dimensions for the virtual stick's ring (where the user will first touch)
	field ringX:float
	field ringY:float
	field ringRadius:float
	Field img_bkg:TImage	
	Field img_front:TImage	
	Field img_state:Bool = False	
	
	' the coordinates and dimensions for the stick (what the user is pushing around)
	' X/Y is relative to the centre of the ring, and positive Y points up
	field stickX:float = 0
	field stickY:float = 0
	field stickRadius:float
	field stickAngle:float
	field stickPower:float
	
	' where the user first touched
	field firstTouchX:float
	field firstTouchY:float
	
	' power must always be >= this, or we return 0
	field deadZone:float
	
	' we need to move the stick this much before it triggers
	field triggerDistance:float = -1
	field triggered:bool = false
	
	' the index of the touch event that initiated the stick movement
	field touchNumber:int = -1
	
	' clips the stick to be within the ring, and updates angles, etc.
	method UpdateStick:void()
		if touchNumber>=0 then
			local length:float = Sqrt(stickX*stickX+stickY*stickY)
			stickPower = length/ringRadius
			if stickPower > 1 then stickPower = 1
			
			if stickPower < deadZone then
				stickPower = 0
				stickAngle = 0
				stickX = 0
				stickY = 0
			else
				if stickX = 0 and stickY = 0 then
					stickAngle = 0
					stickPower = 0
				elseif stickX = 0 and stickY > 0 then
					stickAngle = 90
				elseif stickX = 0 and stickY < 0 then
					stickAngle = 270
				elseif stickY = 0 and stickX > 0 then
					stickAngle = 0
				elseif stickY = 0 and stickX < 0 then
					stickAngle = 180
				elseif stickX > 0 and stickY > 0 then
					stickAngle = ATan(stickY/stickX)
				elseif stickX < 0 then
					stickAngle = 180+ATan(stickY/stickX)
				else
					stickAngle = 360+ATan(stickY/stickX)
				end
				if length > ringRadius then
					stickPower = 1
					stickX = Cos(stickAngle) * ringRadius
					stickY = Sin(stickAngle) * ringRadius
				end
			end
		end
	end
	
public

	' the angle in degrees that the user is pushing, going counter-clockwise from right
	method GetAngle:float()
		return stickAngle
	end
	
	' the strength of the movement (0 means dead centre, 1 means at the edge of the ring (or past it)
	method GetVelocity:float()
		return stickPower
	end
	
	' based on the angle and velocity, get the DX
	method GetDX:float()
		return Cos(stickAngle) * stickPower
	end
	
	' based on the angle and velocity, get the DY
	method GetDY:float()
		return Sin(stickAngle) * stickPower
	end
	
	Method SetSkin:Void(bkg:String,frnt:String)
		Self.img_bkg = IMAGES.Find(bkg)
		Self.img_front = IMAGES.Find(frnt)
		Self.img_state = True
	End Method
	
	' we just touched the screen at point (x,y), so start "controlling" if we touched inside the ring
	method StartTouch:void(x:float, y:float, touchnum:int)
		if touchNumber < 0 then
			if (x-ringX)*(x-ringX) + (y-ringY)*(y-ringY) <= ringRadius*ringRadius then
				touchNumber = touchnum
				firstTouchX = x
				firstTouchY = y
				triggered = false
				if triggerDistance <= 0 then
					triggered = true
					stickX = x-ringX
					stickY = ringY-y
				end
				UpdateStick()
			end
		end
	end
	
	' a touch just moved, so we may need to update the stick
	method UpdateTouch:void(x:float, y:float, touchnum:int)
		if touchNumber>=0 and touchNumber = touchnum then
			if not triggered then
				if (x-firstTouchX)*(x-firstTouchX)+(y-firstTouchY)*(y-firstTouchY) > triggerDistance*triggerDistance then
					triggered = true
				end
			end
			if triggered then
				stickX = x - ringX
				stickY = ringY - y
				UpdateStick()
			end
		end
	end
	
	' we just released a touch, which may have been this one
	method StopTouch:void(touchnum:int)
		if touchNumber>=0 and touchNumber = touchnum then
			touchNumber = -1
			stickX = 0
			stickY = 0
			stickAngle = 0
			stickPower = 0
			triggered = false
		end
	end
	
	method DoRenderRing:void()
		RenderRing(ringX, ringY)
	end
	
	method DoRenderStick:void()
		RenderStick(ringX+stickX, ringY-stickY)
	end
	
	Method Render:Void()		
		Self.DoRenderRing()
		Self.DoRenderStick()
	End Method
	
	' draws the stick (may be overridden to do images, etc.)
	method RenderStick:void(x:float, y:float)	
		If Self.img_state Then
			Self.img_front.Draw(x,y)
		Else
			DrawCircle(x, y, stickRadius)	
		EndIf
		
	end
	
	' draws the outside ring (may be overridden to do images, etc.)
	method RenderRing:void(x:float, y:float)
		If Self.img_state Then			
			Self.img_bkg.Draw(x,y)
		Else
			DrawCircle(x, y, stickRadius)	
		EndIf
	end
	
	' set the location and radius of the ring
	method SetRing:void(ringX:float, ringY:float, ringRadius:float)
		self.ringX = ringX
		self.ringY = ringY
		self.ringRadius = ringRadius
	end
	
	' set the location and radius of the stick
	method SetStick:void(stickX:float, stickY:float, stickRadius:float)
		self.stickX = stickX
		self.stickY = stickY
		self.stickRadius = stickRadius
	end
	
	method SetDeadZone:void(deadZone:float)
		self.deadZone = deadZone
	end
	
	method SetTriggerDistance:void(triggerDistance:float)
		self.triggerDistance = triggerDistance
	end
	
end

